#include "sqlconnpool.h"
#include "log/Logger.h"
#include <functional>
#include "json/value.h"
#include "json/json.h"
#include "config/ConfigManager.h"
#include "sqlconn.h"
#include <thread>
using std::string;
using namespace std;


CSqlConnPool* CSqlConnPool::m_pInstance = nullptr;
std::mutex g_mtx; //全局锁


CSqlConnPool*  CSqlConnPool::GetInstance(int maxCapacity)
{
    if(m_pInstance == nullptr)
    {
        g_mtx.lock();
        if(m_pInstance == nullptr)
        {
            m_pInstance = new CSqlConnPool(maxCapacity);
        }
        g_mtx.unlock();
        return m_pInstance;
    } 
    else
    {
        return m_pInstance;
    }
}
//加载配置文件
bool CSqlConnPool::loadConfigFile()
{
    LOG_INFO("Start LoadConfile Config!");
    //加载配置文件
    CConfigManager* pInstanceCfg = CConfigManager::instance();
    if( nullptr != pInstanceCfg)
    {
        Json::Value temp = Json::nullValue;
        if(pInstanceCfg->getConfig("MySqlDCfg", temp))
        {
            LOG_INFO("MyMySqlDCfg: " << temp.toStyledString());
            if(!temp.isNull() && temp.isMember("ip"))
            {
                m_ip = temp["ip"].asString();
            }
            else if (!temp.isNull() && temp.isMember("port"))
            {
                m_port = temp["port"].asInt();
            }
            else if (!temp.isNull() && temp.isMember("username"))
            {
                m_user = temp["username"].asString();
            }
            else if (!temp.isNull() && temp.isMember("password"))
            {
                m_pwd = temp["password"].asString();
            }
            else if(!temp.isNull() && temp.isMember("dbName"))
            {
                m_dbName = temp["dbName"].asString();
            }
            else if (!temp.isNull() && temp.isMember("initSize"))
            {
                m_initSize = temp["initSize"].asInt();
            }
            else if (!temp.isNull() && temp.isMember("maxIdleTime"))
            {
                m_maxIdleTime = temp["maxIdleTime"].asInt();
            }
            else if (!temp.isNull() && temp.isMember("connTimeOut"))
            {
                m_conTimeCount = temp["connTimeOut"].asInt();
            }
          
        }
        return true;
    }
    else 
    {
        return false;
    }
}

//构造函数
CSqlConnPool::CSqlConnPool(int maxCapacity):m_connQue(maxCapacity)
{
    LOG_INFO("CSqlConnPool::CSqlConnPool......");
    if(!loadConfigFile()) //加载配置文件失败
    {
        LOG_INFO("LoadConfigFile failed!");
        return;
    }
    for(size_t i=0; i < m_initSize; i++)
    {
        addConn();
    }

    std::thread produce(std::bind(&CSqlConnPool::produceConnTask, this));
    produce.detach();
    
    std::thread scanner(std::bind(&CSqlConnPool::scannerConnTask, this));
    scanner.detach();

}
//析构函数
CSqlConnPool::~CSqlConnPool()
{
    ;
}
void CSqlConnPool::produceConnTask()
{
    for(;;)
    {
        addConn();
    }
}
void CSqlConnPool::scannerConnTask()
{
    for(;;)
    {
        std::this_thread::sleep_for(std::chrono::microseconds(m_maxIdleTime));
        while(m_connQue.getSize() > m_initSize)
        {
            CSqlConn* p= m_connQue.front();
            if(p->getAliveTime() >= m_maxIdleTime)
            {
                CSqlConn *temp = nullptr;
                m_connQue.pop(temp);
                delete temp;
                temp = nullptr;
            }
            else 
                break;
        }
    }
}


void CSqlConnPool::addConn()
{
   // std::shared_ptr<CSqlConn> m_connQue = std::make_shared<CSqlConn>();
   CSqlConn* conn = new CSqlConn();
   conn->connect(m_ip, m_port, m_user, m_pwd, m_dbName);
   conn->refreshAliveTime();
   m_connQue.PushBack(conn);
}

std::shared_ptr<CSqlConn> CSqlConnPool::getConn()
{
    CSqlConn* conn;
    if(!m_connQue.pop(conn, m_conTimeCount))
    {
        LOG_INFO("Get Connection Timeout");
        return nullptr;
    }
    std::shared_ptr<CSqlConn> sp(conn,
                    [&](CSqlConn *p)
                    {
                        p->refreshAliveTime();
                        m_connQue.PushBack(p);
                    });
    return sp;
}